package com.vladmr.sleepytimepicker.provider;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import com.vladmr.sleepytimepicker.AppPreferences;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.util.Log;

public class SleepyTimeProvider extends ContentProvider {
	private static final String TAG = "sleepytimepicker";
	private static final String AUTHORITY = "com.vladmr.sleepytimepicker.provider.sleepytimeprovider";

	public static class Matcher {
		private static final int FALLASLEEP = 1;
		private static final int FALLASLEEP_ITEM = 2;
		private static final int WAKEUP = 3;
		private static final int WAKEUP_ITEM = 4;
	}

	public static class Columns {
		public static final String ID = "_id";
		public static final String TIME_OFFSET = "long_offset";
		public static final String TIME_FALLASLEEP = "long_fallasleep";
		public static final String SLEEPYTIME = "long_sleepytime";
		public static final String TIME_WAKEUP = "long_wakeup";	
	}

	private List<Calendar> sleepyTimeList;
	private int sleeptimecount = 6;
	protected AppPreferences appPrefs;

	// it is your desired sleep cycle duration
	private int stageHour, stageMinute;

	// alarm time
	private int alarmHour, alarmMinute;

	// you are going to go to bed after this time
	private int offsetHour, offsetMinute;

	@Override
	public boolean onCreate() {
		sleepyTimeList = new ArrayList<Calendar>();
		return true;
	}

	private void LoadValuesFromPrefs() {
		// loading values from AppPreferences
		appPrefs = new AppPreferences(getContext());
		stageHour = appPrefs.getStageHour();
		stageMinute = appPrefs.getStageMinute();
		alarmHour = appPrefs.getAlarmHour();
		alarmMinute = appPrefs.getAlarmMinute();
		offsetHour = appPrefs.getOffsetHour();
		offsetMinute = appPrefs.getOffsetMinute();
	}

	// Creates a UriMatcher object.
	private static final UriMatcher sUriMatcher = buildUriMatcher();

	private static UriMatcher buildUriMatcher() {
		UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

		// all of the content URI patterns, that the provider should recognize
		matcher.addURI(AUTHORITY, "fallasleep", Matcher.FALLASLEEP);
		matcher.addURI(AUTHORITY, "fallasleep/#", Matcher.FALLASLEEP_ITEM);
		matcher.addURI(AUTHORITY, "wakeup", Matcher.WAKEUP);
		matcher.addURI(AUTHORITY, "wakeup/#", Matcher.WAKEUP_ITEM);
		return matcher;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		String[] columns;
		MatrixCursor cursor;
		int i;
		Object[] obj;

		LoadValuesFromPrefs();
		InitSleepTimeArray();

		Log.i(TAG, "uri to match: " + uri.toString());
		switch (sUriMatcher.match(uri)) {
		case Matcher.FALLASLEEP:
			columns = new String[] { Columns.ID, Columns.TIME_FALLASLEEP,
					Columns.SLEEPYTIME, Columns.TIME_WAKEUP, Columns.TIME_OFFSET };
			cursor = new MatrixCursor(columns);
			for (i = sleeptimecount - 1; i >= 0; i--) {
				obj = GetFallasleepItem(i);
				if (obj != null) {
					cursor.addRow(obj);
				}
			}
			Log.i(TAG, "FALLASLEEP response OK, " + cursor.getCount());
			return cursor;
		case Matcher.FALLASLEEP_ITEM:
			columns = new String[] { Columns.ID, Columns.TIME_FALLASLEEP,
					Columns.TIME_WAKEUP };
			cursor = new MatrixCursor(columns);
			i = Integer.parseInt(uri.getLastPathSegment());
			obj = GetFallasleepItem(i);
			if (obj != null) {
				cursor.addRow(new Object[] { obj[0], obj[1], obj[3] });
			}
			Log.i(TAG, "FALLASLEEP_ITEM response OK, " + cursor.getCount());
			return cursor;
		case Matcher.WAKEUP:
			columns = new String[] { Columns.ID, Columns.TIME_FALLASLEEP,
					Columns.SLEEPYTIME, Columns.TIME_WAKEUP };
			cursor = new MatrixCursor(columns);
			for (i = 0; i < sleeptimecount; i++) {
				obj = GetWakeupItem(i);
				if (obj != null) {
					cursor.addRow(obj);
				}
			}
			Log.i(TAG, "WAKEUP response OK, " + cursor.getCount());
			return cursor;
		case Matcher.WAKEUP_ITEM:
			columns = new String[] { Columns.ID, Columns.TIME_FALLASLEEP,
					Columns.TIME_WAKEUP };
			cursor = new MatrixCursor(columns);
			i = Integer.parseInt(uri.getLastPathSegment());
			obj = GetWakeupItem(i);
			if (obj != null) {
				cursor.addRow(new Object[] { obj[0], obj[1], obj[3] });
			}
			Log.i(TAG, "WAKEUP_ITEM response OK, " + cursor.getCount());
			return cursor;
		default:
			// throw new IllegalArgumentException("Unknown URL " + uri);
			return null;
		}
	}

	@Override
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// This code does not support inserting
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// This code does not support deleting
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		// TODO Auto-generated method stub
		return 0;
	}

	private void InitSleepTimeArray() {
		sleepyTimeList.clear();
		Calendar sleepytime = Calendar.getInstance();
		sleepytime.set(Calendar.HOUR_OF_DAY, 0);
		sleepytime.set(Calendar.MINUTE, 0);
		for (int i = 0; i < sleeptimecount; i++) {
			sleepytime.add(Calendar.HOUR_OF_DAY, stageHour);
			sleepytime.add(Calendar.MINUTE, stageMinute);

			// new object in order to correctly save calendar object (otherwise
			// updates calendar reference in array)
			Calendar time = Calendar.getInstance();
			time.set(Calendar.HOUR_OF_DAY, sleepytime.get(Calendar.HOUR_OF_DAY));
			time.set(Calendar.MINUTE, sleepytime.get(Calendar.MINUTE));
			sleepyTimeList.add(time);
		}
	}

	private Object[] GetFallasleepItem(int i) {
		Calendar fallasleep_time = GetFallasleepTime(sleepyTimeList.get(i));
		Calendar wakeup_time = Calendar.getInstance();
		wakeup_time.set(Calendar.HOUR_OF_DAY, alarmHour);
		wakeup_time.set(Calendar.MINUTE, alarmMinute);
		if (wakeup_time.before(Calendar.getInstance())) {
			wakeup_time.add(Calendar.DAY_OF_MONTH, 1);
		}
		Calendar left_time = GetLeftTime(fallasleep_time);

		if (fallasleep_time.after(Calendar.getInstance())) {
			return new Object[] { i, fallasleep_time.getTimeInMillis(),
					sleepyTimeList.get(i).getTimeInMillis(),
					wakeup_time.getTimeInMillis(), left_time.getTimeInMillis() };
		} else {
			return null;
		}
	}

	private Object[] GetWakeupItem(int i) {
		Calendar fallasleep_time = Calendar.getInstance();
		Calendar wakeup_time = GetWakeupTime(sleepyTimeList.get(i));

		return new Object[] { i, fallasleep_time.getTimeInMillis(),
				sleepyTimeList.get(i).getTimeInMillis(),
				wakeup_time.getTimeInMillis() };
	}

	private Calendar GetFallasleepTime(Calendar sleepy_time) {
		Calendar fallasleep_time = Calendar.getInstance();
		fallasleep_time.set(Calendar.HOUR_OF_DAY, alarmHour);
		fallasleep_time.set(Calendar.MINUTE, alarmMinute);
		if (fallasleep_time.before(Calendar.getInstance())) {
			fallasleep_time.add(Calendar.DAY_OF_MONTH, 1);
		}

		fallasleep_time.add(Calendar.HOUR_OF_DAY,
				(-1) * sleepy_time.get(Calendar.HOUR_OF_DAY));
		fallasleep_time.add(Calendar.MINUTE,
				(-1) * sleepy_time.get(Calendar.MINUTE));
		return fallasleep_time;
	}

	private Calendar GetWakeupTime(Calendar sleepy_time) {
		Calendar wakeup_time = Calendar.getInstance();
		wakeup_time.add(Calendar.HOUR_OF_DAY,
				sleepy_time.get(Calendar.HOUR_OF_DAY));
		wakeup_time.add(Calendar.HOUR_OF_DAY, offsetHour);
		wakeup_time.add(Calendar.MINUTE, sleepy_time.get(Calendar.MINUTE));
		wakeup_time.add(Calendar.MINUTE, offsetMinute);
		return wakeup_time;
	}

	private Calendar GetLeftTime(Calendar fallasleep_time) {
		Calendar left_time = Calendar.getInstance();
		Calendar now = Calendar.getInstance();
		left_time.set(
				Calendar.HOUR_OF_DAY,
				fallasleep_time.get(Calendar.HOUR_OF_DAY)
						- now.get(Calendar.HOUR_OF_DAY));
		left_time.set(Calendar.MINUTE, fallasleep_time.get(Calendar.MINUTE)
				- now.get(Calendar.MINUTE));
		return left_time;
	}
}
